/*
 *解析文件
 *1.将所有.html文件名写入数组
 *2.按照数组里文件名提取文件内容
 *3.将提取到的文件内容放入raw.txt文件中
 */
#include<iostream>
#include<string>
#include<vector>
#include<boost/filesystem.hpp>
#include<fstream>
#include"util.hpp"
const std::string src_path = "data/input";
const std::string output = "data/raw_html/raw.txt";
typedef struct DocInfo{
    std::string title;//标签
    std::string content;//内容
    std::string url;//url
}DocInfo_t;
bool EnumFile(const std::string& src_path,std::vector<std::string>* file_list);
bool ParserFile(const std::vector<std::string>& file_list,std::vector<DocInfo_t>* results);
bool SaveFile(const std::vector<DocInfo>&results,const std::string& output);
void Debug(const std::vector<std::string>& file_list);
int main()
{
    std::vector<std::string> file_list;
    //1.将.html文件名放入数组中，统计所有的.html文件
    if(!EnumFile(src_path,&file_list))
    {
        std::cerr<<src_path<<":文件读取失败！"<<std::endl;
        return 1;
    }
    //Debug(file_list);
    //读取文件内容
    std::vector<DocInfo_t> results;
    if(!ParserFile(file_list,&results))
    {
        std::cerr<<src_path<<":文件读取失败！"<<std::endl;
        return 2;
    }
    //保存读取内容到磁盘文件中
    if(!SaveFile(results,output))
    { 
        std::cerr<<src_path<<":文件读取失败！"<<std::endl;
        return 3;
    }
    return 0;
}

bool EnumFile(const std::string& src_path,std::vector<std::string>* file_list)
{
    //1.创建path对象
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);
    //2.判断src_path路径是否存在
    if(!fs::exists(root_path))
    {
        std::cerr<<src_path<<"not exists!"<<std::endl;
        return false;
    }
    //3.创建一个空的迭代器
    fs::recursive_directory_iterator end;
    //4.循环path对象,遍历里面所有的文件判断是否合法,合法则读取文件内容,在循环中，需要判断是否为普通文件，是否是.html结尾的文件，最后将合法文件名放入file_list数组中
    for(fs::recursive_directory_iterator iter(root_path);iter != end;iter++)
    {
        if(!fs::is_regular_file(*iter))
        {
            continue;
        }
        if(iter->path().extension()!=".html")
        {
            continue;
        }
        file_list->push_back(iter->path().string());
        // std::cout<<iter->path().string()<<std::endl;
    }
    return true;
}
//静态函数，其他文件不可用，静态函数具有内部链接属性，这意味着每个源文件中的静态函数是独立的，只在该源文件内可见。
static bool ParserTitle(const std::string& result,std::string* title)
{
    auto begin = result.find("<title>");
    if(begin == std::string::npos)
        return false;
    begin += std::string("<title>").size();
    auto end = result.find("</title>");
    if(end == std::string::npos)
        return false;
    if(begin > end)return false;
    *title = result.substr(begin,end-begin);
    return true;
}
static bool ParserContent(const std::string& result,std::string* content)
{
    //采用状态机的思想
    enum class Status{
        LABLE,
        CONTENT
    };
    Status s = Status::LABLE;
    for(auto c:result)
    {
        switch(s)
        {
            case Status::LABLE:
                if(c == '>')s = Status::CONTENT;
                break;
            case Status::CONTENT:
                if(c == '<')s = Status::LABLE;
                else
                {
                    //不保留原始文件的'\n',后面作为分隔符使用
                    if(c == '\n')c = ' ';
                    content->push_back(c);
                }
                break;
            default:
            break;
        }
    }
    return true;
}

static bool ParserUrl(const std::string& file,std::string* url)
{
    std::string url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";
    std::string url_tail = file.substr(src_path.size());
    *url = url_head+url_tail;
    return true;
}
void Show(DocInfo_t * doc)
{
    std::cout<<"Title: "<<doc->title<<std::endl;
    std::cout<<"Content: "<<doc->content<<std::endl;
    std::cout<<"Url: "<<doc->url<<std::endl;
}
bool ParserFile(const std::vector<std::string>& file_list,std::vector<DocInfo_t>* results)
{
    
     //int count = 1;
    for(const std::string& file:file_list)
    {   
        std::string s;
        if(!ns_util::FileUtil::ReadFile(file,&s))
        {
            continue;
        }
        DocInfo_t doc;
        //1.先解析title
        if(!ParserTitle(s,&doc.title))
        {
            continue;
        }
        //2.解析content
        if(!ParserContent(s,&doc.content))
        {
            continue;
        }
        //3.解析url
        if(!ParserUrl(file,&doc.url))
        {
            continue;
        }
        //for debug
        //while(count>0)
        //{
        //    Show(&doc);
        //    count--;
       // }
            //Show(&doc);
        results->push_back(std::move(doc));
    }
    return true;
}
bool SaveFile(const std::vector<DocInfo>&results,const std::string& output)
{
#define SEP "\3"
    std::ofstream ofs(output,std::ios::binary|std::ios::out);
    if(!ofs.is_open())
    {
        std::cerr<<"SaveFile: "<<output<<":file open error"<<std::endl;
        return false;
    }
    for(auto& item: results)
    {
        std::string out_string;
        out_string += item.title+SEP;
        out_string += item.content+SEP;
        out_string += item.url+"\n";
        ofs.write(out_string.c_str(),out_string.size());
    }
    ofs.close();
    return true;
}


void Debug(const std::vector<std::string>& file_list)
{
    for(auto& c:file_list)
    {
        std::cout<<c<<std::endl;
    }
}
